package distributedlock.redis;

import java.util.Random;

import org.apache.log4j.Logger;

import util.Configure;
import util.RedisUtil;

/**
 * 
 * 简易的分布式锁（如果Redis本身是比较大的集群可以考虑使用Redisson）
 *
 */
public class RedisLock {
	private Logger log = Logger.getLogger(RedisLock.class);

	public RedisLock() {

	}
	
	public RedisLock(String suffix) {
		this.key = this.key + ":" + suffix;
	}

	private static final Configure INS = Configure.getInstans();

	/**
	 *  默认超时时间（秒） 
	 *  超时会抛弃当前锁里的所有线程
	 *  必要时需要补偿机制
	 */
	public static final long DEFAULT_TIMEOUT = Long.parseLong(INS.getValue("DEFAULT_TIMEOUT")) * 1000L;
	/**
	 *  锁的超时时间（秒），过期删除 
	 */
	public static final int LOCK_TIMEOUT = Integer.parseInt(INS.getValue("LOCK_TIMEOUT"));
	
	private static final long ONE_MILLI_NANOS = 1000000L;
	private static final long  NANOS_TIMEOUT = DEFAULT_TIMEOUT * ONE_MILLI_NANOS;
	
	// 锁状态标志
	private boolean locked = false;
	// 加锁标志
	private static final String LOCKED = "TRUE";
	
	private String key = "LOCK";
	private RedisUtil redisUtil = RedisUtil.getInstans();
	
	private static final Random RANDOM = new Random();
	
	public boolean lock() {
		//System.out.println("RedisLock lock");
		log.info("RedisLock lock");
		long nano = System.nanoTime();
		log.info("RedisLock lock nano=" + nano);
		try {
			while ((System.nanoTime() - nano) < NANOS_TIMEOUT) {
				if (redisUtil.setnx(key, LOCKED) == 1) {
					redisUtil.expire(key, LOCK_TIMEOUT);
					locked = true;

					log.info("RedisLock lock : Get Lock key=" + key);

					return locked;
				}
				// 短暂休眠，nano避免出现活锁
				Thread.sleep(5, RANDOM.nextInt(500));
			}
		} catch (Exception e) {
			e.printStackTrace();
			log.error("RedisLock lock Get Lock Fail", e);
		}
		return false;
	}

	// 无论是否加锁成功，必须调用
	public void unlock() {
		//System.out.println("unlock");
		log.info("RedisLock unlock");
		if (locked) {
			redisUtil.del(key);
			log.info("RedisLock unlock : Del Lock key=" + key);
		}
	}
}